﻿<img src="http://habrastorage.org/storage2/59e/ee4/6fc/59eee46fc539e760a1bd06c92fb9c15a.png" alt="Green-forest Logo"/>
<p>
Хочу рассказать Java-сообществу Хабра о небольшом, но очень полезном (на личном опыте) фреймворке под названием <a target="_blank" href='http://code.google.com/p/green-forest'>Green-forest</a>. Данный фреймворк можно использовать как самостоятельно, так и в контексте JEE или Spring. 

Как с помощью него можно упростить код приложения узнаем под катом.

<habracut>

<p>В начале рассмотрим проблему для решения которой предназначен Green-forest.

<h2>Проблема</h2>
<p>Очень часто архитектура крупного проекта имеет следующий вид:
<ul>
<li>Слой представления</li>
<li>Бизнес-слой</li>
<li>Слой работы с данными</li>
</ul>

<p>При этом бизнес-слой и слой данных состоят из классов-сервисов, предоставляющих внешнему миру публичное API.
Пример такого сервиса:
<source lang="java">
public interface UserService {
	
	User createUser(Object someData);
	
	void activateUser(long id);
	
	User updateUser(User updatedUser);
	
	void blockUser(long id);
	
	void unblockUser(long id);

}
</source>

<p>С ростом приложения растет количество методов в подобных сервисах - а значит растет громоздкость класса-реализации:
<source lang="java">
public class UserServiceImpl implements UserService {

	//метод на 1-ой строке класса
	public User createUser(Object someData) {
		//реализация на 10-15 строк
	}

	//метод 16-ой страке класса
	public void activateUser(long id) {
		//реализация на 10-15 строк
	}

	//и т.д.

	//метод на 1000-ой строке класса
	public void unblockUser(long id) {
		//реализация на 10-15 строк
	}

}
</source>

<p>В итоге появляются классы, содержащие десятки методов и 1000-ти строк кода. Искать и поддерживать код в таких классах становится трудно и муторно.

<h2>Классическое решение</h2>

<p>Очевидным решением видится разделение одного большого сервиса на множество более мелких:
<source lang="java">
public interface UserBasicService {
	
	User createUser(Object someData);
	
	void activateUser(long id);
	
	User updateUser(User updatedUser);

}

public interface UserModerationService {
	
	void blockUser(long id);
	
	void unblockUser(long id);

}</source>

<p>Однако, помимо простоты такой подход имеет ряд недостатков:

<b>Трудность равномерного разделения</b>: бывает трудно разделить сервис на равные части. В одном новом сервисе остается 3 метода, в другом - 10-ть.

<b>Неоднозначность разделения</b>: бывает трудно определить в какой дочерний сервис перенести конкретный метод. В результате методы переносятся нелогично и API усложняется для понимания.

<b>Устранение симптомов, но не причины:</b> по сути, мы не решили проблему, т.к. с ростом методов в новых сервисах они тоже рискуют превратиться в громоздкие 1000-ти строчные реализации.

<p>Попробуем решить проблему громоздких сервисом в новом ключе.

<h2>Решение через атомарные функции</h2>
<p>Раз наше простое разделение сервиса на несколько ему подобных не дает полного решения проблемы, зададимся вопросом, а можем ли мы разделить его на более атомарные сущности? Конечно же можем! Ведь целью любого сервиса являются его методы, а значит, создав множество отдельных методов, мы избавимся от озвученных выше недостатков:

<b>Нет проблем неравномерного и неоднозначного разделения</b>: множество методов разделено равномерно и однозначно по своему определению.

<b>Решена проблема роста:</b> при появлении новых методов мы просто создаем новые классы, не увеличивая размер старых.

<h2>Реализация решения в Green-forest</h2>

<p>В Green-forest Framework применяется архитектура Action-Handler, иллюстрируемая схемой:
<img src="http://habrastorage.org/storage2/5a2/12a/e40/5a212ae40d06c3bd4aee9106f093fa08.png"/>

<p>Рассмотрим компоненты этой схемы:

<ol>
<li>
<h3>Action</h3>
Класс Action представляет атомарный "метод", который можно "вызвать". Внутри себя класс содержит входные и выходные данные: 
<source lang="java">
//Класс Action с типом входных данных - String и выходных - Integer
public class SomeAction extends Action&lt;String, Integer&gt;{
 
    public SomeAction(String input) {
        super(input);
    }
     
}</source>
</li>
<li>
<h3>Handler</h3>
Класс Handler содержит конкретную реализацию для данного Action типа:
<source lang="java">
@Mapping(SomeAction.class)
public class SomeHandler extends Handler&lt;SomeAction&gt;{
 
    @Inject
    SomeService service;
     
    public void invoke(SomeAction action) throws Exception {
     
        String input = action.getInput();
        Integer result = service.doSomeWork(input);
         
        action.setOutput(result);
    }
}</source>
	
</li>
<li>
<h3>Framework</h3>
Объект фреймворка Engine обеспечивает связь между Action и Handler объектами:
<source lang="java">
//создаем объект Engine
Engine engine = new Engine();
 
//регистрируем обработчик
engine.putHandler(SomeHandler.class);
 
//выполняем действие
Integer result = engine.invoke(new SomeAction("some data"));
</source>
</li>
</ol>


<h2>Пример использования</h2>
<p>Итак, мы рассмотрели ключевые классы фреймворка. Используя их, проведем рефакторинг примера с нашим сервисом UserService.

<p>В начале отнаследуем UserService от служебного интерфейса ActionService:
<source lang="java">
import com.gf.service.ActionService;

public interface UserService extends ActionService { 

	//интерфейс не содержит методов - они отнаследованы от ActionService
}
</source>

<p>Далее создадим множество Action классов:
<source lang="java">
import com.gf.Action;

public class CreateUser extends Action&lt;Object, User&gt; {
    
    public CreateUser(Object someData){
        this(someData);
    }
}

public class ActivateUser extends Action&lt;Long, Void&gt; {
    
    public ActivateUser(Long id){
        this(id);
    }
}

/*
и т.д для классов:
	UpdateUser.java
	BlockUser.java
	UnblockUser.java
	...
*/
</source>

<p>Осталось создать обработчики для данных классов. Например:
<source lang="java">
@Mapping(CreateUser.class)
public class CreateUserHandler extends Handler&lt;CreateUser&gt;{
 
    public void invoke(CreateUser action) throws Exception {
     
        Object input = action.getInput();
        User user = ... 
        action.setOutput(user);
    }
}</source>

<p>Пример конечного вида реализации UserServiceImpl:
<source lang="java">
import com.gf.Action;
import com.gf.core.Engine;

public class UserServiceImpl implements UserService {
	
	Engine engine;

	public UserServiceImpl(){
		
		engine = new Engine();
		
		//регистрируем Handler классы по имени пакета
		engine.scanAndPut("some.package");
	}

	public &lt;I, O&gt; O invoke(Action&lt;I, O&gt; action) {
		return engine.invoke(action);
	}

	public &lt;I, O&gt; O invokeUnwrap(Action&lt;I, O&gt; action) throws Exception {
		return engine.invokeUnwrap(action);
	}

}

</source>

<p>В итоге мы разделили большой интерфейс UserService на множество Action классов, а реализацию UserServiceImpl - на множество Handler классов.

<h2>Выводы и ссылки</h2>
<p>Мы рассмотрели проблему концентрации логики в одном классе и способы решения, включая разделение на атомарные классы-методы. Далее мы познакомились с Green-forest Framework, который реализует последний подход с помощью архитектуры Action-Handler. Используя Green-forest, вы можете упростить и структурировать код своего приложения.

Приятной работы, коллеги!

<p>Ссылки:
<a target="_blank" href='http://code.google.com/p/green-forest'>Green-forest Framework</a>
<a target="_blank" href='http://green-forest.googlecode.com/svn/tags/0.9/reference-guide/single-page.html'>Документация</a>
